﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
using SerialPortTester;

namespace SensorClient
{
    public class SensorPanda : SensorConsumer
    {
        protected const int readerBufferSize = 256;
        protected char[] reader = new char[readerBufferSize];
        protected int readerCursor = 0;
        protected int readerChars = 0;
        protected int readerStart = 0;
        protected bool doAcq = false;
        protected bool debugLevel = false;

        #region SupportMethods
        // Extrage valoarea dintre tagurile senzorului. Sterge inregistrarea din buffer.
        // Returneaza:
        // - true in cazul in care gaseste tagul din parametri, si stringul reply completat
        // - false in caz ca nu il gaseste
        protected bool ExtractReply(string first, string second, out string reply)
        {
            reply = "";
            bool reset = false;
            bool retValue = false;
            string readerString = new string(reader);
            int firstIndex = readerString.IndexOf(first);
            int secondIndex = readerString.IndexOf(second);
            if ((firstIndex != -1) & (secondIndex != -1))
            {
                if (secondIndex > firstIndex)
                {
                    // Extract reply
                    reply = readerString.Substring(firstIndex + first.Length, secondIndex - firstIndex - first.Length);
                    retValue = true;
                }
            }
            return retValue;
        }

        
        // Detecteaza modulul PSAssist pe porturile de tip serial
        // Returneaza null in caz ca nu gaseste modulul sau un string continand portul
        protected bool DetectPSAssist(out string portName)
        {
            string[] ports = SerialPort.GetPortNames();
            SerialPort SPort2;
            bool found = false;
            portName = "";

            // List all serial ports found if debugging
            if (debugLevel)
            {
                Console.Write("Serial ports found:");
                foreach (string port in ports)
                {
                    // Open Port and see if we receive data
                    Console.Write(" " + port);
                }
                Console.WriteLine();
            }

            foreach (string port in ports)
            {
                if (debugLevel)
                {
                    Console.WriteLine("Trying port " + port);
                }

                SPort2 = new SerialPort(port);
                SPort2.ReadTimeout = 500;
                SerialPortFixer.Execute(port);
                SPort2.Open();
                if (SPort2.IsOpen)
                {
                    // Detect PSAssist Module
                    SPort2.Write("id;");
                    Thread.Sleep(200);
                    try
                    {
                        readerChars = SPort2.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        continue;
                    }

                    string reply;
                    if (ExtractReply("<id>", "</id>\r\n", out reply))
                    {
                        portName = port;
                        found = true;
                        Console.WriteLine("Detected PSAssist module on port " + portName);
                    }
                    SPort2.Close();
                    SPort2.Dispose();
                }
                if (found)
                {
                    break;
                }
            }
            return found;
        }
        #endregion SupportMethods

        #region Constructors
        /// <summary>
        /// Creates a SensorPanda object
        /// </summary>
        /// <param name="packageInterval">Acquisition period of the package</param>
        public SensorPanda(int packageInterval, string comPort)
            : base(packageInterval, comPort)
        {
            //This should be empty
        }
        #endregion Constructors

        /// <summary>
        /// The method is used by the package generation thread.It will
        /// generate an event every <param name="packageInterval"></param>
        /// seconds of which args contain the package values.
        /// </summary>
        protected override void DoAcquistion()
        {
            Console.WriteLine("SensorPanda thread started");
            const int writerBufferSize = 256;
            SerialPort SPort;
            string PSAssistPort;
            char[] writer = new char[writerBufferSize];
            bool portFound;
            string dataFoto;
            string dataRef;
            string dataId;
            string dataAmbTemp;
            string dataPress;
            int tmp = 0;
            int index = 0;

            //portFound = DetectPSAssist(out PSAssistPort);
            //PSAssistPort = "COM4"; portFound = true;
            PSAssistPort = this.serialPort;
            portFound = true;

            /*
            if (PSAssistPort != null)
            {
                SPort = new SerialPort(PSAssistPort);
                SPort.ReadTimeout = 5000;
                SerialPortFixer.Execute(PSAssistPort);
                SPort.Open();
                GC.SuppressFinalize(SPort);
                GC.SuppressFinalize(SPort.BaseStream);
            }
            */

            while (this.isOn == true)
            {
                if (portFound)
                {
                    string reply;
                    int answerTimeout = 1000;
                    int answerDelay = 500; // leave at 500. Otherwise it messes up strings

                    // Aquire device id
                    #region readId
                    SPort = new SerialPort(PSAssistPort);
                    SPort.ReadTimeout = answerTimeout;
                    SerialPortFixer.Execute(PSAssistPort);
                    SPort.Open();
                    SPort.Write("id;");
                    Thread.Sleep(answerDelay);
                    try
                    {
                        readerChars = SPort.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        Console.WriteLine("Timeout while reading id");
                        break;
                    }
                    if (ExtractReply("<id>", "</id>\r\n", out reply))
                    {
                        Console.WriteLine("Retrieved value of id: " + reply);
                        dataId = reply;
                    }
                    else
                    {
                        Console.WriteLine("id format invalid");
                        Console.ReadLine();
                        break;
                    }
                    SPort.Close();
                    SPort.Dispose();
                    #endregion readId

                    // Aquire foto sensor
                    #region readFoto
                    SPort = new SerialPort(PSAssistPort);
                    SPort.ReadTimeout = answerTimeout;
                    SerialPortFixer.Execute(PSAssistPort);
                    SPort.Open();
                    SPort.Write("foto;");
                    Thread.Sleep(answerDelay);
                    try
                    {
                        readerChars = SPort.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        Console.WriteLine("Timeout while reading id");
                        break;
                    }
                    if (ExtractReply("<foto>", "</foto>\r\n", out reply))
                    {
                        Console.WriteLine("Retrieved value of foto: " + reply);
                        dataFoto = reply;
                    }
                    else
                    {
                        Console.WriteLine("foto format invalid");
                        Console.ReadLine();
                        break;
                    }
                    SPort.Close();
                    SPort.Dispose();
                    #endregion readFoto

                    // Aquire ref sensor
                    #region readRef
                    SPort = new SerialPort(PSAssistPort);
                    SPort.ReadTimeout = answerTimeout;
                    SerialPortFixer.Execute(PSAssistPort);
                    SPort.Open();
                    SPort.Write("ref;");
                    Thread.Sleep(answerDelay);
                    try
                    {
                        readerChars = SPort.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        Console.WriteLine("Timeout while reading ref");
                        break;
                    }
                    if (ExtractReply("<ref>", "</ref>\r\n", out reply))
                    {
                        Console.WriteLine("Retrieved value of ref: " + reply);
                        dataRef = reply;
                    }
                    else
                    {
                        Console.WriteLine("ref format invalid");
                        Console.ReadLine();
                        break;
                    }
                    SPort.Close();
                    SPort.Dispose();
                    #endregion readRef

                    // Aquire Ambient temp sensor
                    #region readAmbTemp
                    SPort = new SerialPort(PSAssistPort);
                    SPort.ReadTimeout = answerTimeout;
                    SerialPortFixer.Execute(PSAssistPort);
                    SPort.Open();
                    SPort.Write("temp;");
                    Thread.Sleep(answerDelay);
                    try
                    {
                        readerChars = SPort.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        Console.WriteLine("Timeout while reading temp");
                        break;
                    }
                    if (ExtractReply("<temp>", "</temp>\r\n", out reply))
                    {
                        Console.WriteLine("Retrieved value of temp: " + reply);
                        dataAmbTemp = reply;
                    }
                    else
                    {
                        Console.WriteLine("temp format invalid");
                        Console.ReadLine();
                        break;
                    }
                    SPort.Close();
                    SPort.Dispose();
                    #endregion readAmbTemp

                    // Aquire Pressure sensor
                    #region readPress
                    SPort = new SerialPort(PSAssistPort);
                    SPort.ReadTimeout = answerTimeout;
                    SerialPortFixer.Execute(PSAssistPort);
                    SPort.Open();
                    SPort.Write("press;");
                    Thread.Sleep(answerDelay);
                    try
                    {
                        readerChars = SPort.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        Console.WriteLine("Timeout while reading press");
                        break;
                    }
                    if (ExtractReply("<press>", "</press>\r\n", out reply))
                    {
                        Console.WriteLine("Retrieved value of press: " + reply);
                        dataPress = reply;
                    }
                    else
                    {
                        Console.WriteLine("press format invalid");
                        Console.ReadLine();
                        break;
                    }
                    SPort.Close();
                    SPort.Dispose();
                    #endregion readPress

                    Console.WriteLine("Sending package to server");
                    
                    #region BuildSensorPackage
                            //EKG
                            for (index = (int)PackageFormat.SensorPositionStart.ECG;
                                 index < PackageFormat.ECG_VECTOR_LENGTH
                                         * PackageFormat.ECG_ELEMENT_SIZE;
                                 index += 2)
                            {
                                tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                                package[index] = (byte)(tmp & (byte)0xFF);
                                package[index + 1] = (byte)(tmp >> 8);
                            }

                            //HTA
                            index = (int)PackageFormat.SensorPositionStart.HTA;
                            //Primary value
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);
                            //Secondary value
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index + 2] = (byte)(tmp & (byte)0xFF);
                            package[index + 3] = (byte)(tmp >> 8);

                            //Pulse
                            index = (int)PackageFormat.SensorPositionStart.PULSE;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Body temperature
                            index = (int)PackageFormat.SensorPositionStart.BODY_TEMPERATURE;
                            //Fixed part
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.EIGHT_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            //Decimal parth
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.FOUR_BITS_MAX);
                            package[index + 1] = (byte)tmp;

                            //Weight
                            index = (int)PackageFormat.SensorPositionStart.WEIGHT;
                            //Fixed part
                            //tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            tmp = int.Parse(dataPress);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);
                            //Decimal part
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.FOUR_BITS_MAX);
                            package[index + 2] = (byte)(tmp & (byte)0xFF);

                            //Glicemia
                            index = (int)PackageFormat.SensorPositionStart.GLICEMY;
                            //tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            tmp = int.Parse(dataPress);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Respiratory frequency
                            index = (int)PackageFormat.SensorPositionStart.RESPIRATORY_FREQUENCY;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.EIGHT_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);

                            //Ambient temperature
                            index = (int)PackageFormat.SensorPositionStart.AMBIENT_TEMPERATURE;
                            tmp = int.Parse(dataAmbTemp);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Humidity sensor
                            index = (int)PackageFormat.SensorPositionStart.HUMIDITY;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Light sensor
                            index = (int)PackageFormat.SensorPositionStart.LIGHT;
                            tmp = int.Parse(dataFoto);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //RFID, Voltage, Gas
                            index = (int)PackageFormat.SensorPositionStart.RFID_VOLTAGE_GAS;
                            
                            //How the boolean values are generated:
                            //7 6 5 4 3   2       1          0
                            //* * * * * GAS_BIT VOLTAGE_BIT RFID_BIT
                            //
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.THREE_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            #endregion BuildSensorPackage
                            
                            Thread.Sleep(packageInterval-1500);

                            SensorsReceivedArgs args = new SensorsReceivedArgs(package);
                            OnSensorsReceived(this, args);
                    
                } // PortAssist
            } // while
        }
    }
}
